package com.logicbus.backend;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.alogic.load.Scanner;
import com.anysoft.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.logicbus.models.catalog.Path;
import com.logicbus.models.servant.ServantCatalog;
import com.logicbus.models.servant.ServiceDescription;
import com.logicbus.models.servant.ServiceDescriptionWatcher;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 服务注册表
 * 
 * <p>
 * 服务注册表用于为服务工厂提供服务规格配置信息
 * 
 * @author duanyy
 * 
 * @since 1.6.7.20
 * @version 1.6.14.12 [20210617 duanyy] <br>
 * - 支持自己的AccessController和Normalizer <br>
 */
public interface ServantRegistry extends Configurable,XMLConfigurable,AutoCloseable,Reportable, Scanner<ServiceDescription> {
	/**
	 * 查找指定服务的服务规格
	 * 
	 * <p>在服务目录中查找指定服务的服务规格配置信息，如果服务不存在，返回为空.
	 * @param id 指定的服务id
	 * @return 服务规格
	 */
	public ServiceDescription get(Path id);
	
	/**
	 * 获取当前服务目录列表
	 * @return 服务目录列表
	 */
	public ServantCatalog[] getServantCatalog();

	public Normalizer getNormalizer(Normalizer dft);

	public AccessController getAccessController(AccessController dft);
	
	/**
	 * 增加监听器
	 * @param watcher 监听器
	 */
	public void addWatcher(ServiceDescriptionWatcher watcher);
	
	/**
	 * 注销监听器
	 * @param watcher 监听器
	 */
	public void removeWatcher(ServiceDescriptionWatcher watcher);

	
	/**
	 * 虚基类
	 * @author duanyy
	 *
	 */
	public static abstract class Abstract implements ServantRegistry,Scanner.Listener<ServiceDescription>{
		/**
		 * a slf4j logger
		 */
		protected static final Logger logger = LoggerFactory.getLogger(ServantRegistry.class);

		protected String id;
		protected Normalizer normalizer = null;
		protected AccessController accessController = null;
		/**
		 * 服务目录列表
		 */
		protected List<ServantCatalog> catalogs = new ArrayList<ServantCatalog>();

		@Override
		public String getId(){
			return id;
		}

		@Override
		public Normalizer getNormalizer(Normalizer dft) {
			return normalizer == null ? dft:normalizer;
		}

		@Override
		public AccessController getAccessController(AccessController dft) {
			return accessController == null?dft:accessController;
		}

		protected Normalizer getNormalizer(){
			return normalizer;
		}

		protected AccessController getAccessController(){
			return accessController;
		}
		
		@Override
		public void configure(Element e, Properties p) {
			Properties props = new XmlElementProperties(e,p);

			Element elem = XmlTools.getFirstElementByPath(e,"normalizer");
			if (elem != null){
				try {
					Normalizer.TheFactory ncf = new Normalizer.TheFactory(Settings.getClassLoader());
					normalizer = ncf.newInstance(elem,props,"module",DefaultNormalizer.class.getName());
				} catch (Throwable t) {
					logger.error("Failed to initialize normalizer:" + XmlTools.node2String(elem));
					logger.error(ExceptionUtils.getStackTrace(t));
				}
			}
			elem = XmlTools.getFirstElementByPath(e,"ac");
			if (elem != null){
				AccessController.TheFactory f = new AccessController.TheFactory();
				try {
					accessController = f.newInstance(elem,props,"module", IpAndServiceAccessController.class.getName());
				} catch (Throwable t) {
					logger.error("Failed to create access controller:" + XmlTools.node2String(elem));
					logger.error(ExceptionUtils.getStackTrace(t));
				}
			}

			loadConfig(props,e);

			configure(props);

			scan(this);
		}

		@Override
		public void configure(Properties p){
			id = PropertiesConstants.getString(p,"id","default");
		}

		@Override
		public void close()  {
			
		}

		@Override
		public void report(Element xml) {
			if (xml != null){
				xml.setAttribute("module", getClass().getName());
			}
		}

		@Override
		public void report(Map<String, Object> json) {
			if (json != null){
				JsonTools.setString(json,"module",getClass().getName());
			}
		}

		protected void loadConfig(Properties p, Element root) {
			if (root == null) {
				return;
			}
			Factory<ServantCatalog> factory = new Factory<ServantCatalog>();
			NodeList children = XmlTools.getNodeListByPath(root,"catalog");
			for (int i = 0; i < children.getLength() ; i++){
				Node item = children.item(i);
				if (item.getNodeType() != Node.ELEMENT_NODE){
					continue;
				}
				Element e = (Element)item;
				try {
					ServantCatalog servantCatalog = factory.newInstance(e, p, "module");
					if (servantCatalog != null){
						catalogs.add(servantCatalog);
					}
				}catch (Exception ex){
					logger.error("Can not create instance of ServantCatalog.", ex);
				}
			}
		}

		/**
		 * 查询指定服务的服务规格
		 * @param id 服务ID
		 * @return 服务规格
		 */
		@Override
		public ServiceDescription get(Path id){
			for (int i = 0 ; i < catalogs.size() ; i ++){
				ServantCatalog __catalog = catalogs.get(i);
				if (__catalog == null){
					continue;
				}
				ServiceDescription sd = __catalog.findService(id);
				if (sd != null){
					return sd;
				}
			}
			return null;
		}

		/**
		 * 获得服务目录列表
		 *
		 * @return 服务目录列表
		 */
		@Override
		public ServantCatalog[] getServantCatalog(){
			return (ServantCatalog[]) catalogs.toArray(new ServantCatalog[0]);
		}

		@Override
		public void addWatcher(ServiceDescriptionWatcher watcher){
			for (ServantCatalog catalog:catalogs){
				if (catalog != null){
					catalog.addWatcher(watcher);
				}
			}
		}

		@Override
		public void removeWatcher(ServiceDescriptionWatcher watcher) {
			for (ServantCatalog catalog:catalogs){
				if (catalog != null){
					catalog.removeWatcher(watcher);
				}
			}
		}

		@Override
		public void scan(Listener<ServiceDescription> listener) {
			for (ServantCatalog catalog:catalogs){
				if (catalog != null){
					catalog.scan(listener);
				}
			}
		}

		@Override
		public Object begin(String scannerId) {
			return this;
		}

		@Override
		public void found(Object cookie, ServiceDescription data) {
			String pattern = data.getPattern();
			logger.info("Service Found:{}({})",data.getPath(), StringUtils.isNotEmpty(pattern)?pattern:"");
			if (normalizer != null && normalizer instanceof PathPatternSupport) {
				PathPatternSupport pp = (PathPatternSupport)normalizer;
				if (StringUtils.isNotEmpty(pattern)) {
					pp.apply(data.getPath(),String.format("%s/%s",data.getPath(),data.getPattern()));
				}
			}
		}

		@Override
		public void end(Object cookie, String scannerId) {

		}
	}
}
